package com.yutian.springbootwebflux.config;

import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.Map;

/**
 * 如果没有这个注解，你自定义的这个可能不会生效。因为 WebFlux 有一个默认的实现 DefaultErrorWebExceptionHandler，它的优先级是 @Order(-1)。所以加上 @Order(-2) 是让我们自己的实现拥有更高的优先级。
 * 在SpringBoot中,默认的全局异常处理类是DefaultErrorWebExceptionHandler,会根据请求头中的Accept参数返回视图或者数据(JSON).
 * 注意，不推荐CustomExceptionHandler，虽然@RestControllerAdvice优先处理异常，但它无法捕获RouterFunctions.route路由的请求异常
 * @author : cuixiuyin
 * @date : 2019/11/2
 */
@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    public GlobalErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ApplicationContext applicationContext, ServerCodecConfigurer configurer) {
        super(errorAttributes, resourceProperties, applicationContext);
        this.setMessageWriters(configurer.getWriters());
    }

    /**
     * errorAttributes 对象将是我们在网络异常处理程序的构造函数传递一个的精确副本。理想情况下，这应该是我们自定义的 Error Attributes 类。然后，我们清楚地表明我们想要将所有错误处理请求路由到 renderErrorResponse() 方法。最后，我们获取错误属性并将它们插入服务器响应主体中。
     *
     * 然后，生成一个 JSON 响应，其中包含错误，HTTP 状态和计算机客户端异常消息的详细信息。对于浏览器客户端，它有一个 whitelabel 错误处理程序，它以 HTML 格式呈现相同的数据。其中的响应信息我们可以根据自己的需要进行定制。
     * @param errorAttributes
     * @return
     */
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(final ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    private Mono<ServerResponse> renderErrorResponse(final ServerRequest request) {
        final Map<String, Object> errorPropertiesMap = getErrorAttributes(request, true);
        return ServerResponse.status(HttpStatus.OK)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(BodyInserters.fromObject(errorPropertiesMap.get("error")));
    }
}